#include	"a_global_vars.h"

void compute_performance(int sd, short event, void* arg) {
	//	if (a_interval_performance->highest_deliverable_inst_id > -1)
	{
		gettimeofday(&performance_end_time, NULL);
		gettimeofday(&endTime, NULL);

		a_interval_performance->interval_time = measure_time_difference(performance_end_time, performance_start_time);
		a_interval_performance->total_time = measure_time_difference(endTime, startTime);
		a_interval_performance->interval_multicast_throughput = calculate_throughput(a_interval_performance->interval_bytes_multicast, a_interval_performance->interval_time);
		a_interval_performance->interval_forward_throughput = calculate_throughput(a_interval_performance->interval_bytes_forward, a_interval_performance->interval_time);
		a_interval_performance->interval_throughput_received_from_proposer = calculate_throughput(a_interval_performance->interval_bytes_received_from_proposer, a_interval_performance->interval_time);
		a_interval_performance->interval_delivery_throughput = calculate_throughput(a_interval_performance->interval_num_packets_delivered * (sizeof(proposer_msg) - PROPOSER_MSG_HEADER_SIZE), a_interval_performance->interval_time);

		a_interval_performance->interval_latency = a_interval_performance->interval_latency / (double) (a_interval_performance->interval_num_packets_delivered * MAX_MSG_COUNT);
		a_interval_performance->interval_proposer_to_multicaster_latency = (a_interval_performance->interval_proposer_to_multicaster_latency) / a_interval_performance->interval_num_packets_proposer_to_multicaster;

		a_store_performance_in_array(&a_performances[0], &a_next_performance_index, a_interval_performance);
		a_print_performance(a_interval_performance);
		a_nulify_performance(a_interval_performance);

		if (a_next_performance_index >= STOPPING_CONDITION) {
			a_store_performance_in_file(a_performance_file, a_latency_file, a_next_performance_index);
			free(data);
			free(paxos_instances);
			exit(1);
		}
	}
	gettimeofday(&performance_start_time, NULL);
	evtimer_add(print_stat_timeout_eve, &tv2);
}
double measure_time_difference(struct timeval now, struct timeval past) {
	struct timeval elapsed_interval;

	elapsed_interval.tv_sec = now.tv_sec - past.tv_sec;
	elapsed_interval.tv_usec = (now.tv_usec > past.tv_usec ? now.tv_usec - past.tv_usec : past.tv_usec - now.tv_usec);
	double msec_interval = 1000 * elapsed_interval.tv_sec + elapsed_interval.tv_usec / (double) 1000;

	return msec_interval;
}

double calculate_throughput(int bytes, double elapsed_time) {
	double thr = (bytes / (double) (1024 * 1024));
	thr = thr / (double) elapsed_time;
	thr = thr * 1000 * 8;
	return thr;
}

void a_store_performance_in_array(a_performance * a_performances_array, int * performance_index, a_performance *i_p) {
	memcpy((a_performances_array + *performance_index), i_p, sizeof(a_performance));
	*(performance_index) = *(performance_index) + 1;
}
void a_print_performance(a_performance *p) {
	printf("------------------------------------------------------------------------------------------------------------------\n");
	printf("TOTAL_TIME:   %f INTERVAL_TIME: %f  TICKS_PER_SECOND: %f\n", p->total_time, p->interval_time, p->interval_num_entrance);
	printf("INTERVAL DELI: 	   NUM_PACKETS: %d, THROUGHPUT: %f\n", p->interval_num_packets_delivered, p->interval_delivery_throughput);
	printf("INTERVAL PROP: 	   BYTES: %f, THROUGHPUT: %f\n", p->interval_bytes_received_from_proposer, p->interval_throughput_received_from_proposer);
	printf("INTERVAL FORW: 	   BYTES: %f, THROUGHPUT: %f\n", p->interval_bytes_forward, p->interval_forward_throughput);
	printf("INTERVAL MULT: 	   BYTES: %f, THROUGHPUT: %f\n", p->interval_bytes_multicast, p->interval_multicast_throughput);
	printf("INTERVAL REDUNDANT PACKETS:     %d  LOST_PACKETS: %d\n", p->interval_num_redundant_packets_received, p->interval_num_packets_lost);
	printf("LATENCY: %f\n\n", p->interval_latency);
}
void a_store_performance_in_file(const char * performance_file, const char * lat_file, int num_entries) {
	FILE *fp;
	int i;
	fp = fopen(performance_file, "a");
	assert(fp != NULL);
	for (i = DISCARD; i < num_entries - DISCARD; i++) {
		fprintf(fp, "%d %d %f %d %f %f %f %f %f %f %f %d %f %d %f %d\n", i + 1, SUBMIT_PER_WAKE, a_performances[i].interval_time, a_performances[i].interval_num_packets_delivered, a_performances[i].interval_delivery_throughput, a_performances[i].interval_bytes_forward, a_performances[i].interval_forward_throughput, a_performances[i].interval_bytes_multicast, a_performances[i].interval_multicast_throughput, a_performances[i].interval_bytes_received_from_proposer, a_performances[i].interval_throughput_received_from_proposer, a_performances[i].interval_num_redundant_packets_received, a_performances[i].interval_num_entrance, a_performances[i].highest_deliverable_inst_id, a_performances[i].interval_latency, a_performances[i].interval_num_packets_lost);
	}
	fclose(fp);
	fp = fopen(lat_file, "w");
	assert(fp != NULL);

	for (i = 0; i < a_interval_performance->latency_counter; i++)
		fprintf(fp, "%f\n", latency[i]);

	fclose(fp);

}
void a_nulify_performance(a_performance *p) {
	p->interval_time = 0;
	p->interval_num_packets_delivered = 0;
	p->interval_delivery_throughput = 0;
	p->interval_num_redundant_packets_received = 0;
	p->interval_num_packets_lost = 0;
	p->interval_num_entrance = 0;
	p->interval_latency = 0;
	p->interval_proposer_to_multicaster_latency = 0;
	p->interval_num_packets_proposer_to_multicaster = 0;
	p->interval_num_packets_lost = 0;
	p->interval_num_overwritten_insts = 0;
	p->interval_multicast_throughput = 0;
	p->interval_bytes_multicast = 0;
	p->interval_bytes_received = 0;
	p->interval_bytes_received_from_proposer = 0;
	p->interval_throughput_received_from_proposer = 0;
	p->interval_bytes_forward = 0;
	p->interval_forward_throughput = 0;

	if (a_next_performance_index == 0) {
		p->latency_counter = 0;
		p->next_instance = 0;
	}
}

